﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using Platform.Storage.Entity;
using FastMember;
using Platform.Storage.Impl;
using Platform.ObjectModel.Design;
using System.Diagnostics;
using Platform.Infrastructure;
using Platform.ObjectModel;

namespace Platform.Linq
{

	/// <summary>
	/// Realiza a execução da pesquisa
	/// </summary>
	public sealed class DynamicQueryExecutor // TODO : Mover para o namespace Platform.Storage
	{
		//private SymbolTable symbols = new SymbolTable();

		/// <summary>
		/// 
		/// </summary>
		/// <param name="materializer"></param>
		internal DynamicQueryExecutor( Library library )
		{
			//Debug.Assert( materializer != null, "O materializador do documento não foi informado." );
			Debug.Assert( library != null, "A biblioteca de documentos não foi informada." );

			this.Library = library;
			this.Translator = new EntityQueryTranslator();
			this.Materializer = new EntityDataObjectMaterializer( library );
		}

		/// <summary>
		/// Obtém ou define o objeto que transforma os dados de chave e valor em um documento
		/// </summary>
		public EntityQueryTranslator Translator
		{ get; private set; }

		/// <summary>
		/// Objeto que irá realiza a materialização dos dados selecionados
		/// </summary>
		public EntityDataObjectMaterializer Materializer
		{ get; private set; }

		/// <summary>
		/// Obtém a biblioteca de documentos
		/// </summary>
		public Library Library
		{ get; private set; }

		/// <summary>
		/// Obtém ou define quantos níveis do grafo serão carregados junto com os dados 
		/// dos documentos selecionados
		/// </summary>
		/// <remarks>Atualmente somente um nível pode ser carregado</remarks>
		//public int EagerLoadLevel
		//{ get; set; }

		/// <summary>
		/// Realiza a tradução e mapeamento dos dados
		/// </summary>
		/// <typeparam name="TResult"></typeparam>
		/// <param name="query"></param>
		/// <returns></returns>
		public TResult Execute<TResult>( Expression query )
		{
			Arguments.IsNotNull( "query", query );
			//Arguments.IsInRange( "EagerLoadLevel", this.EagerLoadLevel, 0, 1 );

			// Tradução da expressão lambda
			var translated = this.Translator.Translate( query );

			if ( translated.Type == typeof( IQueryable<FieldValue> ) )
			{
				// TODO : Remover a compilação desse passo
				var lambda = Expression.Lambda<Func<IQueryable<FieldValue>>>( translated );
				// Realiza a pesquisa
				var items = lambda.Compile()();

				// Cria o objeto para realizar a enumeração
				Type resultType = typeof( TResult );
				Type itemType = null;

				var context = this.Translator.QueryContext;

				if ( resultType.IsEnumerable() )
				{
					itemType = resultType.GetGenericArguments()[ 0 ];
					return ( TResult )Activator.CreateInstance( typeof( DynamicQueryEnumerable<> ).MakeGenericType( itemType ), this.Materializer, items, this.GetRelationsEnumerator( items, context ) );
				}
				else
				{
					var enumerator = new DynamicQueryEnumerable<TResult>( this.Materializer, items, this.GetRelationsEnumerator( items, context ) );
					return enumerator.FirstOrDefault();
				}
			}
			else
			{
				var lambda = Expression.Lambda<Func<TResult>>( translated );
				return lambda.Compile()();
			}
		}


		private RelationshipEnumerator GetRelationsEnumerator( IQueryable<FieldValue> parentFields, Context translatorContext )
		{
			if ( translatorContext.LoadProperties.Any() )
			{
				return RelationshipEnumerator.Create( this.Materializer, translatorContext.EntityType, translatorContext.Resolver, parentFields );
			}
			else
			{
				return new RelationshipEnumerator();
			}

			//if ( this.EagerLoadLevel > 0 )
			//{
			//	return RelationshipEnumerator.Create( this.Materializer, translatorContext.EntityType, translatorContext.Resolver, parentFields );
			//}
			//else
			//{
			//	return new RelationshipEnumerator();
			//}
		}
	}
}
